<script src='http://shjs.sourceforge.net/lang/sh_javascript.js'></script>
<script src='http://shjs.sourceforge.net/sh_main.js'></script>
<link rel="stylesheet" href="../style/style.css">
<link rel='stylesheet' type='text/css' href='../style/code.css'>

<body onload="sh_highlightDocument('sh', 'javascript');">
<div id='supercontainer' ><title>
Starting Anymote
</title>
<h1 id="exercise-5.-starting-an-anymote-session">Exercise 5. Starting an Anymote session</h1>
<h2 id="goal">Goal</h2>
<ul>
<li>Start an Anymote session with your Google TV</li>
</ul>
<p>In this exercise, we will be implementing the &quot;Start Anymote&quot; button session.</p>
<center>
<img src="../images/start_anymote_button.png" width="70%" alt="Start Anymote button" title="Start Anymote button">
</center>

<p>and the &quot;Reestablish existing connection&quot; button.</p>
<center>
<img src="../images/existing_device_button.png" width="70%" alt="Existing connections button" title="Existing connections button">
</center>

<p>Although we'll cheat a bit on the second one as it has nothing to do with the NPAPI library functions. It just shows more detailed usage of methods you already know.</p>
<h2 id="prep">Prep</h2>
<ul>
<li>Completed Exercise 1 <a href="install_extensions.html">Installing the AnymoteLearningExercise extension</a></li>
<li>Completed Exercise 2 <a href="initialize_gtvremote.html">Initializing the GTVRemote object</a></li>
<li>Completed Exercise 3 <a href="discovery.html">Discovery</a></li>
<li>Completed Exercise 4 <a href="pairing.html">Pairing</a></li>
<li>Open the anymote_session_controller.js file in the text editor of your choice</li>
</ul>
<h2 id="starting-an-anymote-session">Starting an Anymote session</h2>
<p>The Anymote session has <a href="http://code.google.com/p/google-tv-chrome-extensions/wiki/AnymoteSession">two functions</a> related to session control, startSession and stopSession.</p>
<table><tbody><tr style="background-color: #ddd"><td style="border: 1px solid #ccc; padding: 5px;"> 
<strong>Returns</strong>
</td><td style="border: 1px solid #ccc; padding: 5px;"> 
<strong>Method</strong>
</td><td style="border: 1px solid #ccc; padding: 5px;"> 
<strong>Description</strong>
</td></tr> <tr><td style="border: 1px solid #ccc; padding: 5px;">  </td><td style="border: 1px solid #ccc; padding: 5px;"> 
startSession(<i>name</i>, <i>version</i>, <i>host</i>, <i>port</i>, <i>callback</i>)
</td><td style="border: 1px solid #ccc; padding: 5px;">
Sends a request to Google TV to start an Anymote session. If the TV and extension are already paired the TV will respond with a CONNECT event. <i>name</i> is a string which is the application name of the Chrome Extension issuing this request. <i>version</i> is the Anymote protocol version that the extension is using (only version 1 exist at present). <i>host</i> is a string which is the IP address of the Google TV, for example '172.17.115.193'. <i>port</i> is a number which is the Anymote session port. Currently all Google TVs use 9551 as the Anymote port, however, the Discovery Client should be used to confirm the Anymote port. <i>callback</i> is a callback function that will receive Anymote events.
</td></tr> <tr><td style="border: 1px solid #ccc; padding: 5px;">  </td><td style="border: 1px solid #ccc; padding: 5px;"> 
stopSession()
</td><td style="border: 1px solid #ccc; padding: 5px;"> 
Ends the Anymote session.
</td></tr> </tbody></table>

<p>Then <a href="http://code.google.com/p/google-tv-chrome-extensions/wiki/AnymoteSession">six functions</a> related to communication, sendKeyEvent, sendMouseMove, sendMouseWheel, sendData, sendFling, and sendPing (which we'll discuss later)</p>
<h3 id="start-pairing-process">1. Start Pairing process</h3>
Open the anymote_session_controller.js file and find the function
<pre class="sh_javascript"><code>var anymoteStartSession = function(googleTvIpAddress, response) {
  displayToast('anymoteStartSession not implemented');

  // TODO: Start an Anymote session.

};
</code></pre>

Change the function to start the Anymote session
<pre class="sh_javascript"><code>var anymoteStartSession = function(googleTvIpAddress, response) {
  anymoteSession.startSession('Anymote Learning Exercise', 1, googleTvIpAddress, 9551,
    function(e) {
      switch (e.type) {
        case googletvremote.anymote.EventType.CONNECTED:
          displayToast('Successfully started Anymote session with ' + googleTvIpAddress);
          document.body.className = 'connected';  // Turns popup body green.
          break;
      }
    });
};
</code></pre>

<p>This is a big step. At this point you can send your first Anymote command. Go through all the steps to pair like before, then start and Anymote session.</p>
<center>
<img src="../images/home_button.png" width="50%" alt="Anymote home button" title="Anymote home button">
</center>

<p>Your popup window should turn green.</p>
<center>
<img src="../images/connected_anymote.png" width="50%" alt="Connected Anymote" title="Connected Anymote">
</center>

<p>Once connected you can try the Anymote button such as Home, Search, etc. You can use all the buttons, but let's clean things up a bit before looking at their code.</p>
<h3 id="reestablish-an-existing-connection">2. Reestablish an existing connection</h3>
As mentioned earlier in the Goals section, for this part we're going to cheat. There are multiple TODOs in these functions
<pre class="sh_javascript"><code>var anymoteConnectToExistingDevice = function() {
  displayToast('anymoteConnectToExistingDevice not implemented');
  // TODO: Pull the paired device list from localStorage
  // TODO: Attempt to pair with the device in index 0
  // TODO: Set a callback that will try the next device if this one fails.
};

var anymoteExistingDeviceResponseHandler = function(e) {
  displayToast('anymoteExistingDeviceResponseHandler not implemented');
  // If the type is googletvremote.anymote.EventType.CONNECTED you are done.
  // TODO: Otherwise attempt to connect using the next device in the array.
};
</code></pre>

And there are a few minor changes to make within the existing anymoteStartSession function. Instead of replacing each function individually just replace the entire contents of anymote_session_controller with
<pre class="sh_javascript"><code>/** Anymote session object that this controller manages. */
var anymoteSession;

/** Array of ip addresses loaded from localStorage that have already paired. */
var pairedDevices = [];

/** Index used when traversing through the list of paired devices. */
var deviceIndex = 0;

/**
 * Start an Anymote session to communicate with Google TV. Before starting an
 * Anymote session, the Google TV at this ip address must already be paired.
 */
var anymoteStartSession = function(googleTvIpAddress, response) {
  // DONE: Set a timeout that will trigger if there is no reply from the TV.
  var anymoteSessionTimeout = window.setTimeout(function() {
    displayToast('Timeout starting Anymote session with ' + googleTvIpAddress);
    document.body.className = '';
    response({type: googletvremote.anymote.EventType.ERROR});
  }, 2000);

  // DONE: Stop the old session if one is already running.
  anymoteSession.stopSession();  // No harm if there is no active connection.

  // DONE: Start an Anymote session.
  anymoteSession.startSession('Anymote Learning Exercise', 1, googleTvIpAddress,
      9551, // Note, assuming the Anymote port is 9551, but in a real app you
      // should use the servicePort returned by the discovery client.
      function(e) {
        switch (e.type) {
          case googletvremote.anymote.EventType.INVALID:
            console.log('Received Anymote session event... INVALID');
            break;
          case googletvremote.anymote.EventType.CONNECTED:
            console.log('Received Anymote session event... CONNECTED');
            window.clearTimeout(anymoteSessionTimeout);
            document.body.className = 'connected'; // Turns popup body green.
            displayToast('Successfully started Anymote session with ' +
                googleTvIpAddress);
            // Make sure this device is at the top of the paired device list.
            pairedDevices = [];
            var devicesInStorage =
                localStorage.getItem(STORAGE_KEY_PAIRED_DEVICES);
            if (devicesInStorage) {
              pairedDevices = JSON.parse(devicesInStorage);
            }
            var indexOfDevice = pairedDevices.indexOf(googleTvIpAddress);
            if (indexOfDevice == -1) {
              // Somehow this device was missing.  Add it to the list.
              pairedDevices.unshift(googleTvIpAddress);  // Adds to beginning.
              localStorage.setItem(STORAGE_KEY_PAIRED_DEVICES,
                  JSON.stringify(pairedDevices));
              updatePairedDevicesList();
            } else if (indexOfDevice != 0) {
              // Move this device into the first slot.
              pairedDevices.splice(indexOfDevice, 1); // Remove.
              pairedDevices.unshift(googleTvIpAddress); // Add to beginning.
              localStorage.setItem(STORAGE_KEY_PAIRED_DEVICES,
                  JSON.stringify(pairedDevices));
              updatePairedDevicesList();
            }
            response({type: googletvremote.anymote.EventType.CONNECTED});
            break;
          case googletvremote.anymote.EventType.ACK:
            console.log('Received Anymote session event... ACK');
            displayToast('Ping ACK.'); // Response to sendPing.
            break;
          case googletvremote.anymote.EventType.DATA:
            console.log('Received Anymote session event... DATA');
            break;
          case googletvremote.anymote.EventType.DATALIST:
            console.log('Received Anymote session event... DATALIST');
            break;
          case googletvremote.anymote.EventType.FLINGRESULT:
            console.log('Received Anymote session event... FLINGRESULT');
            if (e.success) {
              console.log('Successful fling for sequence = ' + e.sequence);
              displayToast('Fling result successful');
            }
            break;
          case googletvremote.anymote.EventType.ERROR:
            console.log('Received Anymote session event... ERROR');
            displayToast('Anymote error ' +
                googletvremote.anymote.ErrorCodeStrings[e.errorCode] +
                ' with ' + googleTvIpAddress);
            console.log('Anymote error ' +
                googletvremote.anymote.ErrorCodeStrings[e.errorCode] +
                ' with ' + googleTvIpAddress);
            if (e.errorCode == googletvremote.anymote.ErrorCode.NOTPAIRED) {
              // Note, often you don't get an ERROR just a timeout.
              window.clearTimeout(anymoteSessionTimeout);
              document.body.className = '';
              response({type: googletvremote.anymote.EventType.ERROR});
            }
            break;
        }
      });
};

/** Go through the paired device list and attempt to start Anymote sessions. */
var anymoteConnectToExistingDevice = function() {
  // DONE: Pull the paired device list from localStorage
  var devicesInStorage = localStorage.getItem(STORAGE_KEY_PAIRED_DEVICES);
  if (devicesInStorage) {
    deviceIndex = 0; // Try the first paired device then move through the list.
    pairedDevices = JSON.parse(devicesInStorage);
    var googleTvIpAddress = pairedDevices[deviceIndex];
    // DONE: Attempt to pair with the device in index 0
    // DONE: Set a callback that will try the next device if this one fails.
    anymoteStartSession(googleTvIpAddress,
        anymoteExistingDeviceResponseHandler);
  } else {
    // displayToast('No devices have been paired yet');
  }
};

/**
 * Calls the next device in the list if this device failed.
 */
var anymoteExistingDeviceResponseHandler = function(e) {
  // If the type is googletvremote.anymote.EventType.CONNECTED you are done.
  // DONE: Otherwise attempt to connect using the next device in the array.
  if (e.type == googletvremote.anymote.EventType.CONNECTED) {
    deviceIndex = -1; // Reset index. Well done on your successful connection.
    return;
  } else {
    // Try the next device in the list if there are more.
    deviceIndex++;
    if (deviceIndex < pairedDevices.length) {
      console.log('Device ' + pairedDevices[deviceIndex - 1] + ' failed. ' +
          'Attempting to connect to device ' + pairedDevices[deviceIndex]);
      anymoteStartSession(pairedDevices[deviceIndex],
          anymoteExistingDeviceResponseHandler);
    } else {
      displayToast('Unable to establish an existing connection');
    }
  }
};

/**
 * Sends an Anymote key event to Google TV.  See the googletvremote_constants
 * for a complete list of keycode values.
 */
var sendAnymoteKeyEvent = function(keycode) {
  anymoteSession.sendKeyEvent(keycode, googletvremote.anymote.Action.DOWN);
  anymoteSession.sendKeyEvent(keycode, googletvremote.anymote.Action.UP);
};

/**
 * Sends a data message of type STRING.
 */
var sendAnymoteStringMessage = function(message) {
  anymoteSession.sendData(googletvremote.anymote.DataType.STRING, message);
};

/**
 * Flings a uri to the Google TV.
 */
var sendAnymoteFling = function(uri, sequenceNumber) {
  sequenceNumber = sequenceNumber || 42; // Sequence number default if not given
  anymoteSession.sendFling(uri, sequenceNumber);
};

/** Sends a ping to the Google TV. Used to test connection status. */
var sendAnymotePing = function() {
  anymoteSession.sendPing();
};
</code></pre>

<p>I know that looks long, but it was just easier to replace the file wholesale. We did this because there were no new NPAPI functions in the &quot;Reestablish an existing connection&quot; code. Less changed than you think, but it was easier to replace everything. If this makes you really angry just replace the three functions anymoteStartSession, anymoteConnectToExistingDevice, and anymoteExistingDeviceResponseHandler. Only those changed.</p>
<p>Anyway connect once more and then you can use the reconnect button in the future. Try it now. Instead of going through the hold process just create a plugin then reconnect.</p>
<center>
<img src="../images/reconnect_buttons.png" width="50%" alt="Reconnection buttons" title="Reconnect"> <img src="../images/connected_anymote.png" width="50%" alt="Connected Anymote" title="Connected Anymote">
</center>

<p>You have successfully started the Anymote session. Let's make one more minor improvement before moving on.</p>
<h2 id="automatic-reconnection-via-popup.js">Automatic reconnection via popup.js</h2>
<p>In a real app you'd automatically do things like creating the plugin and starting an Anymote session with a paired Google TV. So open the popup.js file and find the bottom of the window.onload function.</p>
<pre class="sh_javascript"><code>/** Register listeners for the button elements. */
window.onload = function() {
  // ... lots of other code

//  googletvremoteInitializePlugin();
//  anymoteConnectToExistingDevice();
};
</code></pre>

Uncomment the two function calls. This presses the &quot;Create a new plugin&quot; and &quot;Reestablish an existing pairing&quot; buttons for you when the popup launches.
<pre class="sh_javascript"><code>/** Register listeners for the button elements. */
window.onload = function() {
  // ... lots of other code

  googletvremoteInitializePlugin();
  anymoteConnectToExistingDevice();
};
</code></pre>

<p>This should help speed up your Anymote testing. Getting connected is now a thing of the past! Let's focus on <b>Using</b> Anymote now.</p>
<center>
<img src="../images/anymote_done.png" width="90%" alt="Anymote started" title="Anymote started">
</center>

<p><a href="../instructions.html">Back to main page</a> <span style="float: right;">Move on to <a href="using_anymote.html">Exercise 6 Using Anymote</a></span></p>
